package io.ooad.jenkins.shuren;

import com.fasterxml.jackson.databind.JsonNode;
import hudson.Extension;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractProject;
import hudson.model.Run;
import hudson.model.TaskListener;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.util.FormValidation;
import jenkins.tasks.SimpleBuildStep;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
import retrofit2.Response;

import javax.servlet.ServletException;
import java.io.IOException;
import java.util.Arrays;

/**
 * Sample {@link Builder}.
 * <p>
 * <p>
 * When the user configures the project and enables this builder,
 * {@link DescriptorImpl#newInstance(StaplerRequest)} is invoked
 * and a new {@link ShurenyunBuilder} is created. The created
 * instance is persisted to the project configuration XML by using
 * XStream, so this allows you to use instance fields (like {@link #username})
 * to remember the configuration.
 * <p>
 * <p>
 * When a build is performed, the {@link #perform} method will be invoked.
 *
 * @author Kohsuke Kawaguchi
 */
public class ShurenyunBuilder extends Builder implements SimpleBuildStep {

    private String file;
    private long timeout;
    private String url;
    private String username;
    private String password;
    private ShurenyunService shurenyunService;
    private TaskListener listener;

    @DataBoundConstructor
    public ShurenyunBuilder() {
        file = getDescriptor().getFile();
        timeout = getDescriptor().getTimeout();
        url = getDescriptor().getUrl();
        username = getDescriptor().getUsername();
        password = getDescriptor().getPassword();
    }

    public String getFile() {
        return file;
    }

    @DataBoundSetter
    public void setFile(String file) {
        this.file = firstNotEmpty(file, getDescriptor().getFile());
    }

    public long getTimeout() {
        return timeout;
    }

    @DataBoundSetter
    public void setTimeout(long timeout) {
        if (timeout == 0) {
            this.timeout = getDescriptor().getTimeout();
        } else {
            this.timeout = timeout;
        }
    }

    public String getUrl() {
        return url;
    }

    @DataBoundSetter
    public void setUrl(String url) {
        this.url = firstNotEmpty(url, getDescriptor().getUrl());
    }

    public String getUsername() {
        return username;
    }

    @DataBoundSetter
    public void setUsername(String username) {
        this.username = firstNotEmpty(username, getDescriptor().getUsername());
    }

    public String getPassword() {
        return password;
    }

    @DataBoundSetter
    public void setPassword(String password) {
        this.password = firstNotEmpty(password, getDescriptor().getPassword());
    }

    public ShurenyunService getShurenyunService() {
        if (shurenyunService == null) {
            shurenyunService = Utils.createShurenyunService(url, timeout);
        }
        return shurenyunService;
    }

    /**
     * We'll use this from the {@code config.jelly}.
     */

    @Override
    public void perform(Run<?, ?> build, FilePath workspace, Launcher launcher, TaskListener listener) {
        this.listener = listener;
        login();
        JsonNode appInfo = Utils.readAppDefinition(new FilePath(workspace, file));
        boolean appExisted = appExisted(appInfo);
        Response<JsonNode> response;
        if (appExisted) {
            response = new UpdateAppCommand(getShurenyunService(), getAid(appInfo), appInfo).execute();
        } else {
            response = new CreateAppCommand(getShurenyunService(), appInfo).execute();
        }
        processError(response.code());
    }

    private String firstNotEmpty(String... files) {
        String first = files[0];
        if (first == null || first.trim().isEmpty()) {
            if (files.length == 1) {
                return first;
            }
            return firstNotEmpty(Arrays.copyOfRange(files, 1, files.length));
        }
        return first;
    }

    private String getAid(JsonNode appInfo) {
        return appInfo.get("id").textValue();
    }

    private boolean appExisted(JsonNode appInfo) {
        Response<JsonNode> response = new GetAppCommand(getShurenyunService(), getAid(appInfo)).execute();
        if (response.code() == 200) {
            return true;
        }
        if (response.code() == 404) {
            return false;
        }
        listener.error("访问数人云失败");
        throw new RuntimeException("访问数人云失败");
    }

    private void login() {
        System.out.println("=============url: " + url);
        System.out.println("=============username: " + username);
        System.out.println("=============password: " + password);
        new LoginCommand(getShurenyunService(), username, password).execute();
    }

    private void processError(int code) {
        if (code == 401) {
            listener.error("未授权");
            throw new RuntimeException("未授权");
        }
        if (code == 403) {
            listener.error("禁止访问");
            throw new RuntimeException("禁止访问");
        }
        if (code == 404) {
            listener.error("应用不存在");
            throw new RuntimeException("应用不存在");
        }
        if (code == 400) {
            listener.error("操作失败");
            throw new RuntimeException("操作失败");
        }
        if (code == 500) {
            listener.error("服务器错误");
            throw new RuntimeException("服务器错误");
        }
    }


    // Overridden for better type safety.
    // If your plugin doesn't really define any property on Descriptor,
    // you don't have to do this.
    @Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl) super.getDescriptor();
    }

    /**
     * Descriptor for {@link ShurenyunBuilder}. Used as a singleton.
     * The class is marked as public so that it can be accessed from views.
     * <p>
     * <p>
     * See {@code src/main/resources/hudson/plugins/hello_world/ShurenyunBuilder/*.jelly}
     * for the actual HTML fragment for the configuration screen.
     */
    @Extension // This indicates to Jenkins that this is an implementation of an extension point.
    public static final class DescriptorImpl extends BuildStepDescriptor<Builder> {
        private String file = "shurenyun.json";
        private long timeout;
        private String url;
        private String username;
        private String password;

        /**
         * In order to load the persisted global configuration, you have to
         * call load() in the constructor.
         */
        public DescriptorImpl() {
            load();
        }

        /**
         * Performs on-the-fly validation of the form field 'name'.
         *
         * @param value This parameter receives the value that the user has typed.
         * @return Indicates the outcome of the validation. This is sent to the browser.
         * <p>
         * Note that returning {@link FormValidation#error(String)} does not
         * prevent the form from being saved. It just means that a message
         * will be displayed to the user.
         */
        public FormValidation doCheckName(@QueryParameter String value)
                throws IOException, ServletException {
            if (value.length() == 0)
                return FormValidation.error("Please set a name");
            if (value.length() < 4)
                return FormValidation.warning("Isn't the name too short?");
            return FormValidation.ok();
        }

        public boolean isApplicable(Class<? extends AbstractProject> aClass) {
            // Indicates that this builder can be used with all kinds of project types 
            return true;
        }

        /**
         * This human readable name is used in the configuration screen.
         */
        public String getDisplayName() {
            return "数人云服务JenkIns插件";
        }

        private <T> T getOrDefault(T value, T defaultValue) {
            if (value == null ) {
                return defaultValue;
            }
            return value;
        }

        public String getFile() {
            return file;
        }

        public long getTimeout() {
            return timeout;
        }

        public String getUrl() {
            return url;
        }

        public String getUsername() {
            return username;
        }

        public String getPassword() {
            return password;
        }
    }
}

